package com.lizemin.mybatis.sqlHandler;

import com.lizemin.mybatis.entity.MapperSql;
import com.lizemin.mybatis.entity.ParamValue;
import com.lizemin.mybatis.entity.SelectSql;
import com.lizemin.mybatis.util.SqlUtil;

import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.*;

/**
 * @author lzm
 * @date 2025/5/20 22:10
 * @description
 */
public class SelectSqlHandler extends SqlHandler {

    public SelectSqlHandler(DataSource dataSource) {
        super(dataSource);
    }

    @Override
    public Object handle(MapperSql mapperSql, HashMap<String, ParamValue> paramValueMap, Class<?> returnType) {
        SelectSql selectSql = (SelectSql) mapperSql;
        String sql = selectSql.getSql();
        List<String> placeholders = SqlUtil.getPlaceholders(sql);
        sql = sql.replaceAll("#\\{[^}]+}", "?");
        try (Connection conn = dataSource.getConnection();
             // 使用 PreparedStatement 预编译 SQL 语句，防止 SQL 注入，且 SQL 来源安全
             PreparedStatement pstmt = conn.prepareStatement(sql)) {
            for (int i = 0; i < placeholders.size(); i++) {
                String placeholder = placeholders.get(i);
                if (placeholder.contains(".")) {
                    String[] strArr = placeholder.split("\\.");
                    String paramName = strArr[0];
                    String paramFieldName = strArr[1];
                    ParamValue paramValue = paramValueMap.get(paramName);
                    Object obj = paramValue.getValue();
                    Field filed = paramValue.getType().getDeclaredField(paramFieldName);
                    filed.setAccessible(true);
                    Object value = filed.get(obj);
                    Class<?> type = filed.getType();
                    if (type.equals(String.class)) {
                        pstmt.setString(i + 1, (String) value);
                    } else if (type.equals(Integer.class) || type.equals(int.class)) {
                        pstmt.setInt(i + 1, (int) value);
                    } else if (type.equals(Long.class) || type.equals(long.class)) {
                        pstmt.setLong(i + 1, (long) value);
                    }
                } else {
                    ParamValue paramValue = paramValueMap.get(placeholder);
                    Class<?> type = paramValue.getType();
                    if (type.equals(String.class)) {
                        pstmt.setString(i + 1, (String) paramValue.getValue());
                    } else if (type.equals(Integer.class) || type.equals(int.class)) {
                        pstmt.setInt(i + 1, (int) paramValue.getValue());
                    } else if (type.equals(Long.class) || type.equals(long.class)) {
                        pstmt.setLong(i + 1, (long) paramValue.getValue());
                    }
                }
            }
            String resultTypeStr = selectSql.getResultType();
            Class<?> resultType = Class.forName(resultTypeStr);
            ArrayList<Field> selectFields = new ArrayList<>();
            Object finalReturnObj = null;
            if (List.class.isAssignableFrom(returnType)) {
                ArrayList<Object> list = new ArrayList<>();
                finalReturnObj = getSqlResult(pstmt, resultType, selectFields, list);
            } else if (Set.class.isAssignableFrom(returnType)) {
                Set<Object> set = new LinkedHashSet<>();
                finalReturnObj = getSqlResult(pstmt, resultType, selectFields, set);
            } else {
                Object returnObj = resultType.newInstance();
                try (ResultSet rs = pstmt.executeQuery()) {
                    setSelectFields(rs, resultType, selectFields);
                    int i = 0;
                    while (rs.next()) {
                        i++;
                        fillProperties(selectFields, rs, returnObj);
                        if (i > 1) {
                            throw new RuntimeException("查询结果不止一条");
                        }
                    }
                }
                finalReturnObj = returnObj;
            }
            return finalReturnObj;
        } catch (SQLException | NoSuchFieldException | IllegalAccessException | InstantiationException |
                 ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static Object getSqlResult(PreparedStatement pstmt, Class<?> resultType, List<Field> selectFields, Collection<Object> sqlResults) throws SQLException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        Object finalReturnObj;
        try (ResultSet rs = pstmt.executeQuery()) {
            setSelectFields(rs, resultType, selectFields);
            while (rs.next()) {
                Object returnObj = resultType.newInstance();
                fillProperties(selectFields, rs, returnObj);
                sqlResults.add(returnObj);
            }
        }
        finalReturnObj = sqlResults;
        return finalReturnObj;
    }

    private static void setSelectFields(ResultSet rs, Class<?> resultType, List<Field> selectFields) throws SQLException, NoSuchFieldException {
        // 获取 ResultSetMetaData 对象
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        // 获取sql中查询的要查询的字段
        for (int i = 1; i <= columnCount; i++) {
            String columnName = metaData.getColumnName(i);
            Field field = resultType.getDeclaredField(columnName);
            selectFields.add(field);
        }
    }

    private static void fillProperties(List<Field> declaredFields, ResultSet rs, Object returnObj) throws SQLException, IllegalAccessException {
        for (Field declaredField : declaredFields) {
            Class<?> fieldType = declaredField.getType();
            if (String.class.equals(fieldType)) {
                String val = rs.getString(declaredField.getName());
                declaredField.setAccessible(true);
                declaredField.set(returnObj, val);
                continue;
            }
            if (Integer.class.equals(fieldType)) {
                Integer val = rs.getInt(declaredField.getName());
                declaredField.setAccessible(true);
                declaredField.set(returnObj, val);
                continue;
            }
            if (Long.class.equals(fieldType)) {
                Long val = rs.getLong(declaredField.getName());
                declaredField.setAccessible(true);
                declaredField.set(returnObj, val);
            }
        }
    }
}
